﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Web;
using System.Web.SessionState;

using SSN.Utilities.Database;
using SSN.Objects;
using SSN.Security;
using SSN.Utilities.Cache;

using SSN.BusinessFramework.Reps;
using SSN.DotNet;

namespace SSN.WebFramework
{
    /// <summary>
    /// Represents various security related functions and objects.
    /// </summary>
    public class SecurityManager
    {
        private const int MAX_ATTEMPTS = 3;
        private const int password_max_age = 90;
        private const int max_attempts = 5;
        private HttpSessionState _session = null;
        private SSNSecurityContext _ctx = null;
        private static string session_user_key { get { return string.Format("{0}_TOKEN", WebSettings.AppName); } }

        /// <summary>
        /// Creates a new SecurityManager given the current Session
        /// </summary>
        /// <param name="Session">Session from the current page view.</param>
        public SecurityManager(HttpSessionState Session)
        {
            if (Session == null)
            {
                throw new ArgumentNullException("No Session found.");
            }

            _session = Session;

            object o = _session[session_user_key];
            if (o != null)
            {
                _ctx = (SSNSecurityContext)o;
            }
        }

        private User _CurrentUser;
        public User CurrentUser
        {
            get
            {
                if (_CurrentUser == null)
                {
                    _CurrentUser = new User();
                    _CurrentUser.Load(SecurityContext, SecurityContext.userid);
                }

                return _CurrentUser;
            }
        }

        /// <summary>
        /// Returns the current user's UserId.
        /// </summary>
        public string UserID { get { return CurrentUser.userid; } }

        /// <summary>
        /// Returns the current user's name.
        /// </summary>
        public string UserName { get { return CurrentUser.userName; } }

        /// <summary>
        /// Returns the current SecurityContext.
        /// </summary>
        public SSNSecurityContext SecurityContext
        {
            get
            {
                if (_ctx == null)
                {
                    if (_session != null && _session[session_user_key] != null)
                    {
                        _ctx = (SSNSecurityContext)_session[session_user_key];
                    }
                }

                return _ctx;
            }
        }

        /// <summary>
        /// Returns whether the user is currently logged in.
        /// </summary>
        public bool IsLoggedIn
        {
            get
            {
                if (SecurityContext == null && WebSettings.SecurityType == WebLoginType.Windows)
                {
                    LogOn(HttpContext.Current.User.Identity.Name.ToString(), "", _session);
                    if (SecurityContext == null)
                    {
                        throw new Exception("Userid:" + HttpContext.Current.User.Identity.Name);
                    }
                }

                return SecurityContext != null;
            }
        }

        /// <summary>
        /// Attempts to login the user using a username and password.
        /// </summary>
        /// <param name="userid">Userid.</param>
        /// <param name="pwd">Password</param>
        /// <returns>Result of the logon attempt.</returns>
        public static LogonResult LogOn(string userid, string pwd, HttpSessionState Session)
        {
            try
            {
                Session[session_user_key] = null;
                SSNSecurityContext ctx = null;

                LogonResult lr = SecurityManager.LogOn(userid, pwd, out ctx);

                switch (lr)
                {
                    case LogonResult.Error:
                    case LogonResult.Failed:
                    case LogonResult.FailedLocked:
                        Session[session_user_key] = null;
                        return lr;
                    case LogonResult.Success:
                    case LogonResult.SuccessNeedsChange:
                        Session[session_user_key] = ctx;
                        return lr;
                }

                return LogonResult.Error;
            }
            catch
            {
                return LogonResult.Error;
            }
        }


        /// <summary>
        /// Attempts to login the user using a username and password.
        /// </summary>
        /// <param name="userid">Userid.</param>
        /// <param name="pwd">Password</param>
        /// <param name="ctx">SecurityContext to set as a result of the logon.</param>
        /// <returns>Result of the logon attempt.</returns>
        public static LogonResult LogOn(string userid, string pwd, out SSNSecurityContext ctx)
        {
            try
            {
                var NewFU = new User();

                bool log_success = false;
                if (WebSettings.SecurityType == WebLoginType.Windows)
                {
                    log_success = NewFU.ADWindowsLogon(userid);
                }
                else
                {
                    log_success = NewFU.LogOn(userid, pwd);
                }

                if (!log_success)
                {
                    ctx = null;
                    return LogonResult.Failed;
                }
                else
                {
                    //check lockout
                    if (WebSettings.SecurityType == WebLoginType.UserPass)
                    {
                        int failed_attempts = NewFU.failed_attempts;

                        if (failed_attempts > MAX_ATTEMPTS)
                        {
                            ctx = null;
                            return LogonResult.FailedLocked;
                        }
                    }

                    //make sure this user has access to the required role
                    var rr = WebSettings.RequiredRole;

                    if (!string.IsNullOrEmpty(rr))
                    {
                        if (!NewFU.IsInRole(rr))
                        {
                            ctx = null;
                            return LogonResult.Failed;
                        }
                    }

                    //no lockout, reset fail count and setup returns
                    //_uCache.cacheItem(NewFU.userid, NewFU);
                    ctx = NewFU;

                    //check must_change
                    if (WebSettings.SecurityType != WebLoginType.Windows && NeedPasswordChange(NewFU))
                    {
                        return LogonResult.SuccessNeedsChange;
                    }

                    return LogonResult.Success;
                }
            }
            catch
            {
                ctx = null;
                return LogonResult.Error;
            }
        }

        public static bool NeedPasswordChange(SSNSecurityContext ctx)
        {
            //FocalwebUser user = ctx.c
            return false;
        }

        /// <summary>
        /// Removes the cached SecurityContext.
        /// </summary>
        /// <returns></returns>
        public bool RefreshCredentials()
        {
            _session.Remove(session_user_key);
            return true;
        }

        /// <summary>
        /// Returns a list of all roles in the system.
        /// </summary>
        /// <returns>A</returns>
        public List<Role> AllRoles
        {
            get
            {
                Role r = new Role();
                return DbManager.BossDb.CreateListFromQueryFormat<Role>(SecurityContext, "SELECT * FROM {0}", r.DataTableName);
            }
        }

        

        /// <summary>
        /// Logs the user off and clears the session of their key and clears user specific caches.
        /// </summary>
        /// <returns></returns>
        public bool LogOff()
        {
            ///TODO Decache stuff

            _session[session_user_key] = null;
            _session.Clear();//just to make sure :)
            UserManager.PurgeUser(_ctx); // clear user cache
            return true;

        }

        /// <summary>
        /// Checks to see if the user needs to change their password.
        /// </summary>
        /// <param name="user"></param>
        /// <returns></returns>
        private bool NeedsPasswordChange(User user)
        {
            return user.must_change_password ||
                user.failed_attempts > max_attempts ||
                (DateTime.Now - user.last_password_change).Days > password_max_age;
        }
    }
}
